﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Audio;
using System.IO;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace projetSupinfoBFG
{
    class MapManager : IDisposable
    {
        private Texture2D[] fonds;
        private SoundEffect droneQuiPasse;
        private SoundEffect Bioscanner;
        private SoundEffect Decompression;
        public Song ExplorationSong;
        private vaisseaux.Vaisseau vaisseaux;
        private List<vaisseaux.Vaisseau> listVaisseaux;
        private Obstacle[,] obstacles;
        public TimeSpan TimeRemaining
        {
            get { return timeRemaining; }
        }
        private const int EntityLayer = 2;
        TimeSpan timeRemaining;
        int propTailleLigne;

        public MapManager(IServiceProvider serviceProvider, Stream fileStream, int levelIndex)
        {
            MediaPlayer.IsRepeating = true;
            content = new ContentManager(serviceProvider, "Content");
            timeRemaining = TimeSpan.FromMinutes(2.0);
            LoadObstacles(fileStream);

            fonds = new Texture2D[4];
            for (int i = 0; i < fonds.Length; ++i)
            {
                fonds[i] = Content.Load<Texture2D>("Backgrounds/fond" + "_" + i );
            }

             //droneQuiPasse = Content.Load<SoundEffect>("Sounds/BuitDrone");
            ExplorationSong = content.Load<Song>("Sounds/exploration");
            MediaPlayer.Play(ExplorationSong);
        }


        public void Update(GameTime gameTime,
            KeyboardState keyboardState,
            GamePadState gamePadState,
            DisplayOrientation orientation)
        {
            timeRemaining -= gameTime.ElapsedGameTime;

            if (timeRemaining < TimeSpan.Zero)
                timeRemaining = TimeSpan.Zero;
        }
        public void Dispose()
        {
            Content.Unload();
        }

        // Level content.        
        public ContentManager Content
        {
            get { return content; }
        }
        ContentManager content;


        #region LoadLevel

        /// <summary>
        /// Iterates over every tile in the structure file and loads its
        /// appearance and behavior. This method also validates that the
        /// file is well-formed with a player start point, exit, etc.
        /// </summary>
        /// <param name="fileStream">
        /// A stream containing the tile data.
        /// </param>
        private void LoadObstacles(Stream fileStream)
        {
            // Load the level and ensure all of the lines are the same length.
            int width;
            List<string> lines = new List<string>();
            using (StreamReader reader = new StreamReader(fileStream))
            {
                string line = reader.ReadLine();
                this.propTailleLigne = line.Length;
                width = line.Length;
                while (line != null)
                {
                    lines.Add(line);
                    if (line.Length != width)
                        throw new Exception(String.Format("The length of line {0} is different from all preceeding lines.", lines.Count));
                    line = reader.ReadLine();
                }
            }

            // Allocate the tile grid.
            obstacles = new Obstacle[width, lines.Count];

            // Loop over every tile position,
            for (int y = 0; y < Height; ++y)
            {
                for (int x = 0; x < Width; ++x)
                {
                    // to load each tile.
                    char tileType = lines[y][x];
                    obstacles[x, y] = LoadObstacle(tileType, x, y);
                }
            }

            // Verify that the level has a beginning and an end.
           /* if (Player == null)
                throw new NotSupportedException("A level must have a starting point.");
            if (exit == InvalidPosition)
                throw new NotSupportedException("A level must have an exit.");
*/
        }

        /// <summary>
        /// Loads an individual tile's appearance and behavior.
        /// </summary>
        /// <param name="obstacleType">
        /// The character loaded from the structure file which
        /// indicates what should be loaded.
        /// </param>
        /// <param name="x">
        /// The X location of this tile in tile space.
        /// </param>
        /// <param name="y">
        /// The Y location of this tile in tile space.
        /// </param>
        /// <returns>The loaded tile.</returns>
        private Obstacle LoadObstacle(char obstacleType, int x, int y)
        {
            switch (obstacleType)
            {
                // Passable space
                case '.':
                    return new Obstacle(null, ObstacleCollision.Passable);

                // slowing block
                case '-':
                    return LoadObstacle("BlockSlowing", ObstacleCollision.Slowing);

                case '~':
                    return LoadVarietyObstacle("BlockSlowing", ObstacleCollision.Slowing);

                // Impassable block
                case '#':
                    return LoadVarietyObstacle("BlockImpass", ObstacleCollision.Impassable);

                case '8':
                    return LoadVarietyObstacle("bordHor", ObstacleCollision.Impassable);
                case '4':
                    return LoadVarietyObstacle("bordVer", ObstacleCollision.Impassable);
                case '6':
                    return LoadVarietyObstacle("bordVer", ObstacleCollision.Impassable);
                case '2':
                    return LoadVarietyObstacle("bordHor", ObstacleCollision.Impassable);
                // Unknown tile type character
                default:
                    throw new NotSupportedException(String.Format("Unsupported tile type character '{0}' at position {1}, {2}.", obstacleType, x, y));
            }
        }

        /// <summary>
        /// Creates a new tile. The other tile loading methods typically chain to this
        /// method after performing their special logic.
        /// </summary>
        /// <param name="name">
        /// Path to a tile texture relative to the Content/Tiles directory.
        /// </param>
        /// <param name="collision">
        /// The tile collision type for the new tile.
        /// </param>
        /// <returns>The new tile.</returns>
        private Obstacle LoadObstacle(string name, ObstacleCollision collision)
        {
            return new Obstacle(Content.Load<Texture2D>("Obstacles/" + name), collision);
        }


        /// <summary>
        /// Loads a tile with a random appearance.
        /// </summary>
        /// <param name="baseName">
        /// The content name prefix for this group of tile variations. Tile groups are
        /// name LikeThis0.png and LikeThis1.png and LikeThis2.png.
        /// </param>
        /// <param name="variationCount">
        /// The number of variations in this group.
        /// </param>
        private Obstacle LoadVarietyObstacle(string baseName/*, int variationCount,*/, ObstacleCollision collision)
        {
           /* Random random = new Random() ;

            int index = random.Next(variationCount);*/
            return LoadObstacle(baseName /*+ index*/, collision);
        }




        #endregion
        #region Bounds and collision

        /// <summary>
        /// Gets the collision mode of the tile at a particular location.
        /// This method handles tiles outside of the levels boundries by making it
        /// impossible to escape past the left or right edges, but allowing things
        /// to jump beyond the top of the level and fall off the bottom.
        /// </summary>
        public ObstacleCollision GetCollision(int x , int y)
        {
            // Prevent escaping past the level ends.
            if (x < 0 || x / Width > Width * propTailleLigne+10)
                return ObstacleCollision.Impassable;
            // Allow jumping past the level top and falling through the bottom.
            if (y < 0 || y / Height >= Height * propTailleLigne)
                return ObstacleCollision.Passable;

            return obstacles[x / Width, y / Height].Collision;
        }

        /// <summary>
        /// Gets the bounding rectangle of a tile in world space.
        /// </summary>        
        public Rectangle GetBounds(int x, int y)
        {
            return new Rectangle(x * Obstacle.Width, y * Obstacle.Height, Obstacle.Width, Obstacle.Height);
        }

        /// <summary>
        /// Width of level measured in tiles.
        /// </summary>
        public int Width
        {
            get { return obstacles.GetLength(0); }
        }

        /// <summary>
        /// Height of the level measured in tiles.
        /// </summary>
        public int Height
        {
            get { return obstacles.GetLength(1); }
        }

        #endregion

        #region Draw

        /// <summary>
        /// Draw everything in the level from background to foreground.
        /// </summary>
        /// 
        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            //spriteBatch.Draw(fonds[3], new Rectangle(0, 0, 1024, 1000), Color.White);
           //for (int i = 0; i <= EntityLayer; ++i)
            //Random rand = new Random();
            spriteBatch.Draw(fonds[3], new Rectangle(0, 0, 800, 600), Color.White);

            DrawObstacles(spriteBatch);

           /* for (int i = EntityLayer + 1; i < fonds.Length; ++i)
                spriteBatch.Draw(fonds[i], Vector2.Zero, Color.White);*/
        }

        /// <summary>
        /// Draws each obstacles in the level.
        /// </summary>
        private void DrawObstacles(SpriteBatch spriteBatch)
        {
            // For each tile position
            for (int y = 0; y < Height; ++y)
            {
                for (int x = 0; x < Width; ++x)
                {
                    // If there is a visible tile in that position
                    Texture2D texture = obstacles[x, y].Texture;
                    if (texture != null)
                    {
                        // Draw it in screen space.
                        Vector2 position = new Vector2(x, y) * Obstacle.Size;
                        spriteBatch.Draw(texture, position, Color.White);
                    }
                }
            }
        }

        #endregion
    
    }
}
